开源|如何开发一个高性能的redis cluster proxy?
Redis cluster的架构要求客户端需要直接与redis集群中的每个节点建立连接,并且当出现新增节点加入、节点宕机failover、slot迁移等事件时,客户端需要能够通过redis cluster协议去更新本地的slot映射表,并且能处理ASK/MOVE语义,因此,我们一般称实现了redis cluster协议的客户端为smart redis client。
Redis cluster最多可以构建超过100个主节点的集群(超过之后gossip协议开销过大,且可能引起集群不稳定),按照单节点10G容量(单实例内存过大可能导致性能下降),单集群最多可以支撑1T左右的容量。
为了更方便的将业务迁移到redis cluster,最期望的是客户端SDK的API完全兼容redis/redis-cluster,spring提供的RedisTemplate是一个很好实现,但是对于没有使用SpringRedisTemplate的项目,很多客户端实现的redis和redis-cluster访问API是不一致的(比如Java中流行的Jedis),这无形中提高了迁移工作的工作量和复杂性,此时redis cluster proxy是不错的选择,有了proxy,就可以像操作单实例redis一样操作redis cluster,客户端程序就不需要做任何的修改。
当然,增加一层proxy,必然会导致性能有一定程度的下降,但是proxy作为无状态的服务,理论上可以水平扩展,并且由于proxy层的存在减少了后端redis server的连接数,在某些极限场景下甚至能提高redis集群整体的吞吐量。此外,基于proxy,我们还可以做很多额外的事情:
比如可以在proxy层做分片逻辑,这样当单集群的redis cluster不满足需求(内存/QPS)时,就可以通过proxy层实现透明的同时访问多个redis cluster集群。 再比如可以在proxy层做双写逻辑,这样在迁移或者拆分缓存类型的redis时,就不需要使用redis-migrate-tool之类的工具进行全量迁移,而只需要按需双写,即可完成迁移。 此外因为proxy实现了redis协议,因此可以在proxy层利用其它存储介质实现redis相关命令,从而可以模拟成redis对外服务。一个典型的场景就是冷热分离存储。
支持设置密码 支持代理到普通redis,也支持代理到redis cluster 支持配置自定义的分片逻辑(可以代理到多个redis/redis-cluster集群) 支持配置自定义的双写逻辑(服务器会识别命令的读写属性,配置双写之后写命令会同时发往多个后端) 支持外部插件,从而可以复用协议解析模块(当前包括camellia-redis-proxy-hbase插件,实现了zset命令的冷热分离存储) 支持在线变更配置(需引入camellia-dashboard) 支持多个业务逻辑共享一套proxy集群,如:A业务配置转发规则1,B业务配置转发规则2(需要在建立redis连接时通过client命令设置业务类型) 对外提供了一个spring-boot-starter,3行代码即可快速搭建一个proxy集群
上行协议解析(IO读写) 协议转发规则匹配(内存计算) 请求转发(IO读写) 后端redis回包解包(IO读写) 后端redis回包下发到客户端(IO读写)
上行协议解析时尽可能的一次性解析多个命令,从而进行规则转发时可以批量进行 往后端redis节点进行转发时尽可能的批量提交,这里除了对来自同一个客户端连接的命令进行聚合,还可以对来自不同客户端连接,但转发目标redis相同时,也可以进行命令聚合
当然,所有这些批量和聚合的操作都需要保证请求和响应的一一对应。
后端操作系统层面的突然宕机proxy层可能无法立即感知(没有收到TCP fin包),导致大量请求在等待回包,虽然proxy层没有阻塞,但是客户端表现为请求超时 proxy在尝试转发请求到B集群时,针对B集群的重新连接请求可能拖慢整个流程 宕机导致的大量异常日志可能会引起服务器性能下降(这是一个容易忽视的地方) pipeline提交上来的请求,99个请求指向A集群,1个请求指向B集群,但是由于B集群的不可用,导致指向B集群的请求迟迟不回包或者异常响应过慢,客户端的最终表现是100个请求全部失败了
设置对异常后端节点的快速失败降级策略,避免拖慢整个服务 异常日志统一管理,合并输出,在不丢失异常信息的情况下,减少异常日志对服务器性能的影响 增加对后端redis的定时探活探测,避免宕机无法立即感知导致业务长时间异常
需要从redis迁移到redis-cluster,但是客户端代码不方便修改 客户端直连redis-cluster,导致cluster服务器连接过多,导致服务器性能下降 单个redis/redis-cluster集群容量/QPS不满足业务需求,使用camellia-redis-proxy的分片功能 缓存类redis/redis-cluster集群拆分迁移,使用camellia-redis-proxy的双写功能 使用双写功能进行redis/redis-cluster的灾备 混合使用分片和双写功能的一些业务场景 基于camellia-redis-proxy的插件功能,开发自定义插件
Redis cluster作为官方推荐的集群方案,越来越多的项目已经或正在迁移到redis cluster,camellia-redis-proxy正是在这样的背景下诞生的;特别的,如果你是一个Java开发者,camellia还提供了CamelliaRedisTemplate这样的方案,CamelliaRedisTemplate拥有和普通Jedis一致的API,提供了mget/mset/pipeline等原生JedisCluster不支持的特性,且提供了和camellia-redis-proxy功能一致的分片/双写等特性。
为了回馈社区,camellia已经正式开源了,想详细了解camellia项目的请点击【阅读原文】访问github,同时附上地址:
https://github.com/netease-im/camellia如果你有什么好的想法或者提案,或者有什么问题,欢迎提交issue与我们交流!
关
于
作
者
推荐阅读